from __future__ import absolute_import, unicode_literals
import requests, json, uuid, time, math
import hashlib
import base64
import hmac
from datetime import datetime, date
from urllib import parse
from pytz import utc
from django.conf import settings
from django.utils import timezone
from celery import shared_task
from fsygManager.PulsarSDK import SendAsyncMessage
from mysqldb.redis import clear_dict

# 禁用安全请求警告
from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

from django_redis import get_redis_connection
from django.conf import settings
import logging

logger = logging.getLogger(__name__)


class AccessToken:
    @staticmethod
    def _encode_text(text):
        encoded_text = parse.quote_plus(text)
        return encoded_text.replace("+", "%20").replace("*", "%2A").replace("%7E", "~")

    @staticmethod
    def _encode_dict(dic):
        keys = dic.keys()
        dic_sorted = [(key, dic[key]) for key in sorted(keys)]
        encoded_text = parse.urlencode(dic_sorted)
        return encoded_text.replace("+", "%20").replace("*", "%2A").replace("%7E", "~")

    @staticmethod
    def create_token(access_key_id, access_key_secret):
        parameters = {
            "AccessKeyId": access_key_id,
            "Action": "CreateToken",
            "Format": "JSON",
            "RegionId": "cn-shanghai",
            "SignatureMethod": "HMAC-SHA1",
            "SignatureNonce": str(uuid.uuid1()),
            "SignatureVersion": "1.0",
            "Timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
            "Version": "2019-02-28",
        }
        # 构造规范化的请求字符串
        query_string = AccessToken._encode_dict(parameters)
        print("规范化的请求字符串: %s" % query_string)
        # 构造待签名字符串
        string_to_sign = (
            "GET"
            + "&"
            + AccessToken._encode_text("/")
            + "&"
            + AccessToken._encode_text(query_string)
        )
        print("待签名的字符串: %s" % string_to_sign)
        # 计算签名
        secreted_string = hmac.new(
            bytes(access_key_secret + "&", encoding="utf-8"),
            bytes(string_to_sign, encoding="utf-8"),
            hashlib.sha1,
        ).digest()
        signature = base64.b64encode(secreted_string)
        print("签名: %s" % signature)
        # 进行URL编码
        signature = AccessToken._encode_text(signature)
        print("URL编码后的签名: %s" % signature)
        # 调用服务
        full_url = "http://nls-meta.cn-shanghai.aliyuncs.com/?Signature=%s&%s" % (
            signature,
            query_string,
        )
        print("url: %s" % full_url)
        # 提交HTTP GET请求
        response = requests.get(full_url)
        if response.ok:
            root_obj = response.json()
            key = "Token"
            if key in root_obj:
                token = root_obj[key]["Id"]
                expire_time = root_obj[key]["ExpireTime"]
                return token, expire_time
        print(response.text)
        return None, None


def CreateToken(obj):
    """请求阿里语音合成Token"""
    access_key_id = obj.get("user")
    access_key_secret = obj.get("key")
    token, expire_time = AccessToken.create_token(access_key_id, access_key_secret)
    print("token: %s, expire time(s): %s" % (token, expire_time))
    if expire_time:
        print(
            "token有效期的北京时间：%s"
            % (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(expire_time)))
        )
    return token, expire_time


# ==================================================================================
# 计划任务
# ==================================================================================
import datetime as dt
from datetime import datetime, timedelta, date
from django.db.models import F
from mysqldb.models import (
    AllinpayPlaceworkitemModel,
    AllinpayPlaceworkitemList,
    AllinpayPlaceworkitemSubmit,
    AllinpayPlaceGrade,
    AllinpayPlaceGradeList,
    AllinpayPlacestaffRole,
    AllinpayPlacestaffList,
    AllinpayPlaceList,
    AllinpayStaffList,
    AllinpayPlaceRange,
    AllinpayPlacedeatList,
)

from .serializers import (
    AllinpayPlaceWorkitemSerializer,
    AllinpayPlaceworkitemListSerializer,
)

from django_celery_beat.models import (
    PeriodicTask,
    ClockedSchedule,
)
from StaffManager.staffSDK import addInter
from mysqldb.tasks import WeAppsUniformSend


@shared_task
def CheckWorkItemAlert(args):
    """工作项事务闹钟"""
    try:
        res = AllinpayPlaceworkitemList.objects.get(id=args)
        s = AllinpayStaffList.objects.get(id=res.onstaff, status=1)
        p = AllinpayPlaceList.objects.get(id=res.pid, status=1)
        # 发送微信消息
    except AllinpayPlaceworkitemList.DoesNotExist as err:
        logger.info(f"[工作项提醒][失败]: 查询工作项事务错误")
        return {"rescode": 505, "resmsg": "工作项事务错误"}
    except AllinpayStaffList.DoesNotExist as err:
        logger.info(f"[工作项提醒][失败]: 查询执行员工状态错误")
        return {"rescode": 505, "resmsg": "员工异常"}
    except AllinpayPlaceList.DoesNotExist as err:
        logger.info(f"[工作项提醒][失败]: 查询场所状态错误")
        return {"rescode": 505, "resmsg": "场所异常"}
    except BaseException as err:
        logger.info(f"[工作项提醒][失败]: 未知错误")
        return {"rescode": 505, "resmsg": "未知错误", "resobj": args}
    else:
        if not s.openid:
            logger.info(f"[工作项提醒][失败]: 查询员工OpenID错误")
            return {"rescode": 505, "resmsg": "员工未绑定微信"}
        r = WeAppsUniformSend.apply_async(
            (
                s.openid,
                # "LoginBefor/callOrder/callOrder", # 呼叫
                f"LoginBefor/works/works?pid={res.pid}",
                "收到客户新订单通知",
                [
                    f"{res.id}",
                    "作业项提醒",
                    f"{p.name}-{s.basename}",
                    f"{date.today()} {res.star}",
                ],
            )
        )
        logger.info(f"[工作项提醒][成功]: {s.phone} {res.id}")
        return {"rescode": 200, "resmsg": "success"}


@shared_task
def CheckWorkItemListAlert():
    """检查工作项事务，发送消息通知"""
    day = date.today()
    hour = datetime.today().hour
    minute = datetime.today().minute
    # print(day, hour, minute)
    res = AllinpayPlaceworkitemList.objects.filter(
        currentdate=day, alarm__hour=hour, alarm__minute=minute, status=1
    ).values_list("id", flat=True)
    # print(res.count())
    for i in list(res):
        CheckWorkItemAlert(i)
        time.sleep(0.1)
    if res.count():
        logger.info(f"[工作项提醒][成功]: {res.count()} 条记录")
    print(f"[工作项提醒][成功]: {res.count()} 条记录")
    return {"rescode": 200, "resmsg": "success"}


@shared_task
def CheckWorkItemList(*args, **kwargs):
    """检查工作项事务-未提交, 自动点检事务"""

    def hasWorkList(d):
        """获取昨天的工作项事务对象"""
        s = AllinpayPlaceworkitemSubmit.objects.all().values_list("wid", flat=True)
        x = AllinpayPlaceworkitemList.objects.filter(currentdate=d, issub=1).exclude(
            id__in=list(s)
        )
        return x

    def hasPlaceStaff(p, s):
        """查询场所绑定用户"""
        try:
            x = AllinpayPlacestaffList.objects.get(pid=p, sid=s)
        except BaseException as err:
            x = None
        return x

    d = date.today() - dt.timedelta(days=1)
    x = hasWorkList(d)
    for i in x:
        u = hasPlaceStaff(i.pid, i.onstaff)
        if not u:
            continue
        addInter(u.id, i.subtype, i.subinter, "工作项未提交", i.id)
        time.sleep(0.1)
    if x.count():
        logger.info(f"[工作项提交检测][成功]: {x.count()} 条记录")
    return {"rescode": 200, "resmsg": "Worker List Not Submit"}


@shared_task
def CheckWorkItemModel(**kwargs):
    """检查工作项模板,自动创建事务"""

    def hasTimeOut(pid):
        """检测门店有效期"""
        try:
            r = AllinpayPlacedeatList.objects.get(pid=pid)
        except BaseException as err:
            logger.warn(f"[工作项模板检测][失败]: 门店 {pid} 门店有效期不存在")
            return False
        else:
            if r.deatdate > date.today():
                return True
            else:
                logger.warn(f"[工作项模板检测][失败]: 门店 {pid} 门店有效期已过期")
                return False

    def hasDayList(d, moon=[], week=[]):
        """判断明天是否创建"""
        if not moon and not week:
            return True
        # d = date.today() + dt.timedelta(days=1)
        day = d.day
        wek = d.weekday()
        if day in moon or wek in week:
            return True
        else:
            return False

    def hasGradeDay(pid, sid, day):
        """查询员工班次列表"""
        r = AllinpayPlaceGradeList.objects.filter(
            pid=pid, staff=sid, today=day
        ).values_list("grade", flat=True)
        return list(r)

    def hasGradeDayInfo(gid):
        """查询员工班次属性"""
        try:
            r = AllinpayPlaceGrade.objects.get(id=gid)
        except BaseException as err:
            # print("Err:", err)
            return {"star": "00:00:00", "end": "23:59:59", "sky": 0}
        else:
            return {
                "star": r.star.strftime("%H:%M:%S"),
                "end": r.end.strftime("%H:%M:%S"),
                "sky": r.sky,
            }

    def hasGradeStaff(d, pid=None, rids=[]):
        """按排班分配"""
        if not pid or not rids:
            return []
        # d = date.today() + dt.timedelta(days=1)
        staff = AllinpayPlaceGradeList.objects.filter(
            pid=pid, today=d, grade__in=rids
        ).values_list("staff", flat=True)
        return list(staff)

    def hasRolseStaff(pid=None, rids=[]):
        """按角色分配"""
        if not pid or not rids:
            return []
        staff = AllinpayPlacestaffRole.objects.filter(
            pid=pid, rid__in=rids
        ).values_list("sid", flat=True)
        return list(staff)

    def hasStaffPostOut(staff):
        """列表去重, 去除离职"""
        staff = list(set(staff))
        staff = list(
            AllinpayPlacestaffList.objects.filter(
                pid=i.get("pid"), status=1, sid__in=staff
            ).values_list("sid", flat=True)
        )
        return staff

    def hasWorkItems(sess):
        """查询工作项"""
        try:
            r = AllinpayPlaceworkitemList.objects.get(**sess)
        except BaseException as err:
            return None
        else:
            return r

    def createItemList(sess):
        """创建工作项"""
        # logger.error(f'[error] {sess}')
        instance = hasWorkItems(
            {
                "title": sess.get("title"),
                "pid": sess.get("pid"),
                "onstaff": sess.get("onstaff"),
                "currentdate": sess.get("currentdate"),
            }
        )
        obj = sess.copy()
        doid = obj.pop("id")
        if not instance:
            # 序列化创建
            serializer = AllinpayPlaceworkitemListSerializer(data=obj)
            serializer.is_valid()
            if not serializer.errors:
                serializer.save()
            else:
                logger.warn(f"创建工作项事务失败 {serializer.errors}")
        else:
            partial = False
            serializer = AllinpayPlaceworkitemListSerializer(
                instance, data=obj, partial=partial
            )
            serializer.is_valid(raise_exception=True)
            serializer.save()

    # 初始化系统参数
    wid = kwargs.get("wid", None)
    pid = kwargs.get("pid", None)
    sid = kwargs.get("sid", None)
    day = kwargs.get("day", None)

    task_day = (
        dt.date.fromisoformat(day) if day else date.today() + dt.timedelta(days=1)
    )

    aKwargs = {}
    aKwargs["status"] = 1
    if pid:
        aKwargs["pid"] = pid
    if wid:
        aKwargs["id"] = wid

    # 获取模板
    logger.info("[celery] [AUTO] 开始准备创建工作项事务")
    sess = AllinpayPlaceworkitemModel.objects.filter(**aKwargs)
    logger.info(f"[celery] [AUTO] {sess.count()}")
    # 计数
    jssw = 0
    # 分配任务
    sers = AllinpayPlaceWorkitemSerializer(sess, many=True).data
    logger.info(f"[celery] [AUTO] {len(sers)}")
    for i in sers:
        logger.info(f"[celery] [workitem] 创建工作项事务 [{i['id']}]")
        if not hasDayList(task_day, moon=i.get("ismoon"), week=i.get("isweek")):
            continue
        if not hasTimeOut(i.get("pid")):
            continue
        # 分配执行人
        staff = []
        if i.get("gradeappoint") and i.get("jobappoint"):
            """排班和角色分配"""
            gstaff = hasGradeStaff(task_day, pid=i.get("pid"), rids=i.get("ongrade"))
            rstaff = hasRolseStaff(pid=i.get("pid"), rids=i.get("onrole"))
            staff += list(set(gstaff) & set(rstaff))  # 计算交集
        elif i.get("gradeappoint"):
            """仅排班分配"""
            staff += hasGradeStaff(task_day, pid=i.get("pid"), rids=i.get("ongrade"))
        elif i.get("jobappoint"):
            """仅角色分配"""
            staff += hasRolseStaff(pid=i.get("pid"), rids=i.get("onrole"))
        elif i.get("onstaff"):
            # 指定人列表
            staff = i.get("onstaff")
            staff = hasStaffPostOut(staff)
            for s in staff:
                i["onstaff"] = s
                i["currentdate"] = task_day
                createItemList(i)
            continue
        elif sid:
            # 指定人列表
            staff = [
                sid,
            ]
            for s in staff:
                i["onstaff"] = s
                i["currentdate"] = task_day
                createItemList(i)
            continue
        else:
            staff = []
            continue

        # if not staff: continue
        # 列表去重, 去除离职
        staff = hasStaffPostOut(staff)
        logger.info(f'[celery] [workitem] 创建工作项事务 [{i.get("id")}] [{staff}]')
        # logger.info(f'[celery] [workitem] {i}')
        # 重建数据
        try:
            for s in staff:
                i["onstaff"] = s
                gl = hasGradeDay(i.get("pid"), s, task_day)
                for gls in gl:
                    glsi = hasGradeDayInfo(gls)
                    # 判断时段
                    aad = 0
                    if not glsi["sky"]:
                        # 常规班次
                        if not (
                            # 任务开始时间位于工作时间
                            (
                                glsi.get("star") <= i["star"]
                                and i["star"] <= glsi.get("end")
                            )
                            # 任务结束时间位于工作时间
                            or (
                                glsi.get("star") <= i["over"]
                                and i["over"] <= glsi.get("end")
                            )
                        ):
                            continue
                    else:
                        # 跨天班次
                        if glsi["star"] <= i["star"] and i["star"] <= "23:59:59":
                            # 当天任务
                            aad = 0
                        elif "00:00:00" <= i["star"] and i["star"] <= glsi["end"]:
                            # 次天任务
                            aad = 1
                        else:
                            continue
                    # 任务日期
                    i["currentdate"] = task_day + dt.timedelta(days=aad)
                    logging.info(f"[celery] [AUTO] 创建任务项:{i}")
                    createItemList(i)
                    jssw += 1

        except BaseException as err:
            logger.error(f"[celery] [AUTO] 创建任务项异常:{err}")
        else:
            logger.info(f'[celery] [workitem] 创建工作项事务 [{i.get("id")}] [{jssw}]')

        time.sleep(0.1)
    logger.info("[celery] [AUTO] 结束创建工作项事务")
    print(f"结束创建工作项事务 模板:{len(sers)}; 事务:{jssw};")
    return {"rescode": 200, "resmsg": "Creater Worker List"}


@shared_task
def has_pid_from_idcode(name, value):
    """根据编码及类型获取pid"""
    try:
        r = AllinpayPlaceRange.objects.get(bartype=name, barcode=value)
    except BaseException as err:
        return None
    else:
        return r.pid


# 查询任务列表
# a = PeriodicTask.objects.all()
# print(a)
